#pragma once

#include <iostream>
#include <memory>
#include "InetAddr.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "Log.hpp"
#include "Connection.hpp"
#include "Protocol.hpp"
#include "Reactor.hpp"

using func_t = std::function<std::string(std::string &)>;

class IOService : public Connection
{
    static const int size = 1024;

public:
    IOService(int sockfd)
    {
        SetNonBloack(sockfd);
        SetSockfd(sockfd);
        SetEvents(EPOLLIN | EPOLLET);
    }
    virtual void Recver() override
    {
        // 读取所有数据
        while (true)
        {
            char buffer[size];
            ssize_t s = recv(Sockfd(), buffer, sizeof(buffer) - 1, 0); // 非阻塞
            if (s > 0)
            {
                buffer[s] = 0;
                // 读取成功了
                Append(buffer);
            }
            if (s == 0)
            {
                // 对端关闭连接
                Excepter();
                return;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    // 发送错误了
                    Excepter();
                    return;
                }
            }
        }

        // 走到此处，本轮数据一定读取完毕了
        std::cout << "outbuffer: \n" << InBuffer() << std::endl;
        // 此时是否读取了一个完整的报文还未知，因此，需要引入协议。
        std::string result;
        if(_on_message) //若方法被设置过
           result = _on_message(InBuffer());
        
        //添加应答信息
        AppendToOuT(result);

        //如何处理写的问题，outbuffer发送给对方的问题
        if(!IsOutBufferEmpty())
        {
            //Sender();  //方案一：直接发送，推荐做法
            //方案二：使能writeable即可，让内核来关心写事件
            GetOwner()->EnableReadWrite(Sockfd(), true, true);
        }
    }
    virtual void Sender() override
    {
        while(true)
        {
            ssize_t n = send(Sockfd(), OutString().c_str(), OutString().size(), 0);
            if(n > 0)
            {
                //成功
                DisCardOutString(n);    //剔除n个
            }
            else if(n == 0)
            {
                break;
            }
            else 
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    //缓冲区写满了，下次再来写
                }
                else if(errno == EINTR)
                {
                    continue;
                }
                else 
                {
                    Excepter();
                    return ;
                }
            }
        }

        //一种：outbuffer empty
        //另一种：发送缓冲区被写满了 && outbuffer没有empty，说明写条件不具备
        //使能sockfd在epoll中的事件
        //写事件关系常常是按需设置的，不能常开启
        //而读事件一般是常开启的
        if(!IsOutBufferEmpty()) //写条件不具备 -- 要用epoll来关心
        {
            //修改对sockfd事件的关心 -- 开启对写事件的关心
            GetOwner()->EnableReadWrite(Sockfd(), true, true);
        }
        else    //变成outbuffer empty的情况
        {   //原本sockfd就为读事件关心的情况，这里是显示地区关闭写关心
            //因为可能情况是从写条件不具备转变到outbuffer empty的情况
            GetOwner()->EnableReadWrite(Sockfd(), true, false);
        }

    }
    virtual void Excepter() override
    {
        //打印日志，差错处理，关闭连接
        //Reactor中移除connection；从内核中移除对fd的关心
        LOG(LogLevel::INFO) << "客户端连接可能结束，进行异常处理: " << Sockfd();
        GetOwner()->DelConnection(Sockfd());

    }
    void RegisterOnMessage(func_t on_message)
    {
        _on_message = on_message;
    }

    ~IOService()
    {
    }

public:
    func_t _on_message;
};
